import { acquireToken as acquireTokenFromUtils } from "@/utils/userInfoUtils";
import { notifications } from "@mantine/notifications";
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import JsonBig from "json-bigint";
import React, { useContext } from "react";

let ApiUtilsContext = React.createContext({});
ApiUtilsContext.displayName = "APIUtilsContext";

const baseUrl = import.meta.env.VITE_API_BASEURL.toString();
const showTanstackQueryTool =
  import.meta.env.VITE_SHOW_TANSTACK_DEVTOOL.toString() === "YES";

function APIUtilsProvider({ children }) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000,
      },
    },
  });

  const acquireToken = async () => {
    try {
      return await acquireTokenFromUtils();
    } catch (th) {
      notifications.show({ title: th.message, color: "red" });
      throw th;
    }
  };

  const tanstackInvalidate = (urlString, query) => {
    if (!urlString) {
      queryClient.invalidateQueries();
    }

    let option = query
      ? { queryKey: [urlString, query] }
      : { queryKey: [urlString] };

    queryClient.invalidateQueries(option);
  };

  const tanstackQuery = (urlString, query, refetchInterval) => {
    const option = {
      queryKey: [urlString, query],
      queryFn: ({ queryKey }) => restGET(queryKey[0], query || {}),
    };
    if (refetchInterval) {
      option.staleTime = refetchInterval;
      option.refetchInterval = refetchInterval;
    }

    const tanstackQuery = useQuery(option);
    return tanstackQuery;
  };

  /* 默认的GET 方法 */
  const restGET = async (urlString, query) => {
    const url = new URL(urlString, baseUrl);
    url.search = new URLSearchParams(query);
    let token = await acquireToken();

    try {
      const result = await fetch(url, {
        method: "GET",
        headers: {
          Authorization: token,
        },
      }).then((res) => {
        if (res.ok) {
          return res.text().then(JsonBig.parse);
        } else {
          return res.text().then((text) => Promise.reject(new Error(text)));
        }
      });

      return result;
    } catch (error) {
      if (JSON.parse(error.message)?.message) {
        notifications.show({
          title: JSON.parse(error.message).message,
          color: "red",
        });
      }
      throw error;
    }
  };

  /* 文件下载方法 */
  const restDownload = async (urlString, query, fileName) => {
    const url = new URL(urlString, baseUrl);
    url.search = new URLSearchParams(query);

    const token = await acquireToken();

    try {
      const response = await fetch(url, {
        method: "GET",
        headers: {
          Authorization: token,
        },
      });

      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(errorText);
      }

      // 获取响应的二进制数据
      const blob = await response.blob();

      // 创建一个临时的 URL
      const downloadUrl = URL.createObjectURL(blob);

      // 创建一个 <a> 元素用于下载文件
      const link = document.createElement("a");
      link.href = downloadUrl;
      link.download = fileName || "downloaded-file";

      // 触发下载
      document.body.appendChild(link);
      link.click();

      // 清理 DOM 和 URL 对象
      document.body.removeChild(link);
      URL.revokeObjectURL(downloadUrl);

      // 返回成功状态
      return {
        success: true,
        message: "File downloaded successfully",
      };
    } catch (error) {
      let errorMessage = "An unexpected error occurred.";
      try {
        const parsedError = JSON.parse(error.message);
        errorMessage = parsedError?.message || errorMessage;
      } catch {
        // Ignore JSON parsing errors
      }

      notifications.show({
        title: errorMessage,
        color: "red",
      });

      throw new Error(errorMessage);
    }
  };

  /* 默认的POST方法 */
  const restPOST = async (urlString, body) => {
    const url = new URL(urlString, baseUrl);
    let token = await acquireToken();

    try {
      const result = await fetch(url, {
        method: "POST",
        body: JSON.stringify(body),
        headers: {
          Authorization: token,
          "Content-Type": "application/json; charset=utf8",
        },
      }).then((res) => {
        if (res.ok) {
          notifications.show({
            title: "操作成功",
            color: "teal",
          });
          return res.text().then((text) => (text ? JsonBig.parse(text) : null));
        } else {
          return res.text().then((text) => Promise.reject(new Error(text)));
        }
      });

      return result;
    } catch (error) {
      if (JSON.parse(error.message)?.message) {
        notifications.show({
          title: JSON.parse(error.message).message,
          color: "red",
        });
      }
      throw error;
    }
  };

  /* 上传文件POST方法 */
  const filePOST = async (urlString, file, paramName = "file") => {
    const url = new URL(urlString, baseUrl);

    let token = await acquireToken();
    const formData = new FormData();
    formData.append(paramName, file); // 将文件添加到 FormData 中

    try {
      const result = await fetch(url, {
        method: "POST",
        body: formData,
        headers: {
          Authorization: token,
        },
      }).then((res) => {
        if (res.ok) {
          return res.text().then((text) => (text ? JsonBig.parse(text) : null));
        } else {
          return res.text().then((text) => Promise.reject(new Error(text)));
        }
      });

      return result;
    } catch (error) {
      if (JSON.parse(error.message)?.message) {
        notifications.show({
          title: JSON.parse(error.message).message,
          color: "red",
        });
      }
      throw error;
    }
  };

  /* 默认的DELETE方法 */
  const restDELETE = async (urlString, body) => {
    const url = new URL(urlString, baseUrl);
    let token = await acquireToken();

    try {
      const result = await fetch(url, {
        method: "DELETE",
        body: JSON.stringify(body),
        headers: {
          Authorization: token,
          "Content-Type": "application/json; charset=utf8",
        },
      }).then((res) => {
        if (res.ok) {
          return res.text().then((text) => (text ? JsonBig.parse(text) : null));
        } else {
          return res.text().then((text) => Promise.reject(new Error(text)));
        }
      });

      return result;
    } catch (error) {
      if (JSON.parse(error.message)?.message) {
        notifications.show({
          title: JSON.parse(error.message).message,
          color: "red",
        });
      }
      throw error;
    }
  };

  /* 默认的PUT方法 */
  const restPUT = async (urlString, body) => {
    const url = new URL(urlString, baseUrl);
    let token = await acquireToken();

    try {
      const result = await fetch(url, {
        method: "PUT",
        body: JSON.stringify(body),
        headers: {
          Authorization: token,
          "Content-Type": "application/json; charset=utf8",
        },
      }).then((res) => {
        if (res.ok) {
          notifications.show({
            title: "操作成功",
            color: "teal",
          });
          return res.text().then((text) => (text ? JsonBig.parse(text) : null));
        } else {
          return res.text().then((text) => Promise.reject(new Error(text)));
        }
      });

      return result;
    } catch (error) {
      if (JSON.parse(error.message)?.message) {
        notifications.show({
          title: JSON.parse(error.message).message,
          color: "red",
        });
      }
      throw error;
    }
  };

  const utils = {
    baseUrl,
    acquireToken,
    restGET,
    restDownload,
    filePOST,
    restPOST,
    restDELETE,
    restPUT,
    tanstackQuery,
    tanstackInvalidate,
  };

  return (
    <QueryClientProvider client={queryClient}>
      {showTanstackQueryTool && <ReactQueryDevtools initialIsOpen={false} />}
      <ApiUtilsContext.Provider value={utils}>
        {children}
      </ApiUtilsContext.Provider>
    </QueryClientProvider>
  );
}

function useAPIUtils() {
  return useContext(ApiUtilsContext);
}

export { useAPIUtils };
export default APIUtilsProvider;
